package com.wjl.tableanalyzier;

import com.wjl.tableanalyzier.mapper.BaseMapper;
import com.wjl.tableanalyzier.strategy.ColumnStrategyRegistry;
import com.wjl.tableanalyzier.strategy.Pair;
import lombok.AllArgsConstructor;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.math.BigInteger;
import java.text.CharacterIterator;
import java.text.StringCharacterIterator;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author wjl
 */
@AllArgsConstructor
@Component
public class Analysis implements CommandLineRunner {

    private final BaseMapper baseMapper;

    private final static Pattern pattern = Pattern.compile("^\\d+$");

    static class Table {
        String tableName;
        Map<String, String> type;
        List<String> index;
        int size;

        public Table(String tableName, Map<String, String> type, List<String> index, int size){
            this.tableName = tableName;
            this.type = type;
            this.index = index;
            this.size = size;
        }

        @Override
        public String toString() {
            return "Table{" +
                    "tableName='" + tableName + '\'' +
                    ", type=" + type +
                    ", index=" + index +
                    '}';
        }
    }

    @Override
    public void run(String... args) {
        List<Map<String, String>> maps = baseMapper.showTables();
        List<String> tables
                = maps.stream().map(m -> m.values().stream().findFirst().orElseThrow(NullPointerException::new)).collect(Collectors.toList());
        List<Table> tableList = new ArrayList<>();
        for (String table : tables) {
            Map<String, String> type = baseMapper.showColumnsFromTable(table)
                    .stream()
                    .collect(Collectors.toMap(x -> x.get("Field").toLowerCase(), x -> x.get("Type").toLowerCase()));
            List<String> index = baseMapper.showIndexsFromTable(table)
                    .stream().map(x -> x.get("Column_name").toLowerCase()).collect(Collectors.toList());
            tableList.add(new Table(table, type, index, getTableSizeByTableName(table)));
        }
        BigInteger bigInteger = new BigInteger("0");
        for (Table table : tableList) {
            int rowCount = table.size * 5000000;
            BigInteger rowSum = BigInteger.valueOf(table.type.values().stream().mapToInt(ColumnStrategyRegistry::getSize).sum())
                    .multiply(BigInteger.valueOf(rowCount));
            BigInteger indexSum = BigInteger.valueOf(table.index.stream().mapToInt(x ->
                    Stream.of(x.split(",")).mapToInt(
                            y -> ColumnStrategyRegistry.getSize(table.type.get(y)))
                            .sum())
                    .sum()).multiply(BigInteger.valueOf(rowCount));
//            BigInteger indexSum = BigInteger.valueOf(0);
            System.out.println(table + " \n" + rowCount + "行占用的空间预计: " + rowSum + " ," + indexSum + " bytes");
            bigInteger = bigInteger.add(rowSum).add(indexSum);
        }
        System.out.println("所有表单行总计: " + bigInteger + " bytes " + humanReadableByteCountBin(bigInteger.longValue()));
    }

    private int getTableSizeByTableName(String table) {
        if(!table.contains("_")){
            return 1;
        }
        int index = table.lastIndexOf("_");
        String size = table.substring(index+1);
        if(pattern.matcher(size).find()) {
            return Integer.parseInt(size);
        }
        return 0;
    }

    public static String humanReadableByteCountBin(long bytes) {
        long absB = bytes == Long.MIN_VALUE ? Long.MAX_VALUE : Math.abs(bytes);
        if (absB < 1024) {
            return bytes + " B";
        }
        long value = absB;
        CharacterIterator ci = new StringCharacterIterator("KMGTPE");
        for (int i = 40; i >= 0 && absB > 0xfffccccccccccccL >> i; i -= 10) {
            value >>= 10;
            ci.next();
        }
        value *= Long.signum(bytes);
        return String.format("%.1f %ciB", value / 1024.0, ci.current());
    }
}
